package com.senyint.sys.utils;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.Column;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConversionException;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.beanutils.converters.BigDecimalConverter;
import org.apache.commons.beanutils.converters.IntegerConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.senyint.sys.utils.io.IOUtil;
import com.senyint.sys.utils.log.Log;
import wang.naifei.core.utils.Assert;

/**
 * @author WangNaiFei
 * @version 1.0
 */
public class POJOUtil {

	private final static Logger log = LoggerFactory.getLogger(POJOUtil.class);

	static {

		ConvertUtils.register(new Converter() {
			@SuppressWarnings("unchecked")
			@Override
			public Date convert(@SuppressWarnings("rawtypes") Class type, Object value) {
				if (value == null) {
					return null;
				} else {
					if (value instanceof Date) {
						return (Date) value;
					} else if (value instanceof String) {
						String s = (String) value;
						String format = "";
						if (s.length() == 0) {
							return null;
						} else if (s.length() == 10 && s.matches("(([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29)")) {
							format = "yyyy-MM-dd";
						} else if (s.length() == 8 && s.matches("^([0-1]?\\d|2[0-3]):([0-5]?\\d):([0-5]?\\d)$")) {
							format = "HH:mm:ss";
						} else if (s.length() == 19) {
							format = "yyyy-MM-dd HH:mm:ss";
						} else if (s.length() == 16) {
							format = "yyyy-MM-dd HH:mm";
						} else if (s.length() == 23) {
							format = "yyyy-MM-dd HH:mm:ss.SSS";
						} else {
							throw new RuntimeException("not date String" + s);
						}
						SimpleDateFormat sdf = new SimpleDateFormat(format);
						try {
							return sdf.parse(s);
						} catch (ParseException e) {
							throw new RuntimeException(e);
						}
					} else {
						return null;
					}
				}
			}
		}, Date.class);

		ConvertUtils.register(new Converter() {
			@SuppressWarnings("unchecked")
			@Override
			public String convert(@SuppressWarnings("rawtypes") Class type, Object value) {
				if (value == null) {
					return null;
				} else {
					if (value instanceof String) {
						return (String) value;
					} else if (value instanceof Date) {
						SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
						return sdf.format(value);
					} else if (value instanceof Clob) {
						try {
							return IOUtil.readerToString(((Clob) value).getCharacterStream());
						} catch (IOException | SQLException e) {
							throw new RuntimeException(e);
						}
					} else if (value instanceof Blob) {
						try {
							return IOUtil.inputStreamToString(((Blob) value).getBinaryStream());
						} catch (IOException | SQLException e) {
							throw new RuntimeException(e);
						}
					} else {
						return value.toString();
					}
				}
			}
		}, String.class);

		IntegerConverter integerConverter = new IntegerConverter();
		ConvertUtils.register(integerConverter, Integer.class);

		BigDecimalConverter bigDecimalConverter = new BigDecimalConverter();
		ConvertUtils.register(bigDecimalConverter, BigDecimal.class);
	}

	/**
	 * map中字符串转Object
	 * 
	 * @param m
	 * @return
	 */
	public static Map<String, Object> mapString2mapObject(Map<String, String> m) {
		HashMap<String, Object> r = new HashMap<String, Object>();
		if (m != null)
			for (String key : m.keySet()) {
				Object value = m.get(key);
				r.put(key, value);
			}
		return r;
	}

	/**
	 * @Title: isGetMethod
	 * @Description: 判断是否是get方法
	 * @author WangNaiFei,WangNaiFei@senyint.com
	 * @date 2017年12月8日 上午11:34:37
	 * @version 1.0
	 * @param m
	 * @return
	 */
	public static boolean isGetMethod(String name) {
		return name.startsWith("get") || name.startsWith("is");
	}

	public static boolean isGetMethod(Method m) {
		return isGetMethod(m.getName());
	}

	// ==========bate==================================================
	/**
	 * @param obj
	 * @return
	 */
	public static Map<String, Object> PojoToMap(Object obj) {
		Map<String, Object> map = null;
		if (obj != null) {
			map = new HashMap<String, Object>();
			Class<?> c = obj.getClass();
			// 定义的所有方法
			Method methods[] = c.getDeclaredMethods();
			for (Method method : methods) {
				// 如果是get方法
				String mname = method.getName();
				if (mname.indexOf("get") == 0) {
					String name = StringUtils.uncapitalize(mname.substring(3));
					// Log.INSTANCE.getLog().debug("methodname:{}",name);
					try {
						Object invoke = method.invoke(obj);
						// hibernate hack
						// if (invoke instanceof JavassistLazyInitializer) {
						// continue;
						// }
						map.put(name, invoke);
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					} catch (IllegalArgumentException e) {
						e.printStackTrace();
					} catch (InvocationTargetException e) {
						e.printStackTrace();
					}
				}
			}
		}
		return map;
	}

	/**
	 * @param map
	 * @param clazz
	 * @return
	 */
	public static <T> T mapToPojo(Map<String, ? extends Object> map, Class<T> clazz) {
		return mapToPojo(map, clazz, false);
	}

	public static <T> T mapToPojo(Map<String, ? extends Object> map, Class<T> clazz, boolean fromRequest) {
		T newInstance = null;
		if (map != null) {
			try {
				org.springframework.util.LinkedCaseInsensitiveMap<Object> caseInsensitiveMap = new org.springframework.util.LinkedCaseInsensitiveMap<Object>();
				caseInsensitiveMap.putAll(map);
				newInstance = clazz.newInstance();
				// apache.commons.beanutils 标准实现
				try {
					BeanUtils.populate(newInstance, map);
				} catch (Exception e) {
					Log.warn(log, "BeanUtils populate exception:{}", e.getMessage());
				}
				Method methods[] = clazz.getMethods();
				for (Method m : methods) {
					String mname = m.getName();
					// 有set方法的才处理 忽略大小写非标实现
					if (mname.startsWith("set")) {
						String key = mname.substring(3);
						Method get = clazz.getMethod("get" + key);
						Object v = get.invoke(newInstance);
						// set 值前 判断 标准实现是否已经赋值
						if (v == null) {
							// 数据里有同名属性
							if (caseInsensitiveMap.containsKey(key)) {
								m.invoke(newInstance, setInvokeMethaValue(m, caseInsensitiveMap.get(key), fromRequest));
							}
							/*
							 * 数据里无同名属性 查看get方法注解属性 数据库字段名与属性名不一致 如数据库字段带下划线
							 */
							else {
								Column annotation = get.getAnnotation(Column.class);
								if (annotation != null) {
									String aname = annotation.name();
									if (caseInsensitiveMap.containsKey(aname)) {
										m.invoke(newInstance, setInvokeMethaValue(m, map.get(aname), fromRequest));
									}
								}
							}
						}
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return newInstance;

	}

	public static <T extends Serializable> T clone(T t) {
		return CloneUtils.clone(t);
	}

	/**
	 * @param method
	 * @param value
	 * @return
	 */
	public static Object setFieldValue(Field method, Object value) {
		return setFieldValue(method, value, false);
	}

	private static Object setFieldValue(Field method, Object value, boolean fromRequest) {
		Object v = null;
		String paramTypeName = method.getType().getSimpleName();
		if (value != null) {
			if ("String".equals(paramTypeName)) {
				v = convert(value, String.class);
			} else if ("Date".equals(paramTypeName)) {
				if (fromRequest && "".equals((String) value)) {
					v = null;
				} else {
					v = convert(value, Date.class);
				}
			} else if ("Integer".equals(paramTypeName) || "int".equals(paramTypeName)) {
				if (fromRequest && "".equals((String) value)) {
					v = 0;
				} else {
					v = convert(value, Integer.class);
				}
			} else if ("BigDecimal".equals(paramTypeName)) {
				if (fromRequest && "".equals((String) value)) {
					v = new BigDecimal(0);
				} else {
					v = convert(value, BigDecimal.class);
				}
			}
		} else {
			if ("int".equals(paramTypeName)) {
				v = 0;
			}
		}
		return v;
	}

	/**
	 * @param method
	 * @param value
	 * @return
	 */
	public static Object setInvokeMethaValue(Method method, Object value) {
		return setInvokeMethaValue(method, value, false);
	}

	private static Object setInvokeMethaValue(Method method, Object value, boolean fromRequest) {
		Object v = null;
		String paramTypeName = method.getParameterTypes()[0].getSimpleName();
		if (value != null) {
			if ("String".equals(paramTypeName)) {
				v = convert(value, String.class);
			} else if ("Date".equals(paramTypeName)) {
				if (fromRequest && "".equals((String) value)) {
					v = null;
				} else {
					v = convert(value, Date.class);
				}
			} else if ("Integer".equals(paramTypeName) || "int".equals(paramTypeName)) {
				if (fromRequest && "".equals((String) value)) {
					v = 0;
				} else {
					v = convert(value, Integer.class);
				}
			} else if ("BigDecimal".equals(paramTypeName)) {
				if (fromRequest && "".equals((String) value)) {
					v = new BigDecimal(0);
				} else {
					v = convert(value, BigDecimal.class);
				}
			} else if ("byte[]".equals(paramTypeName)) {
				v = value;
			}
		} else {
			if ("int".equals(paramTypeName)) {
				v = 0;
			}
		}
		return v;
	}

	/**
	 * @param source
	 * @param keyColmun
	 * @param valueColmun
	 * @param typeColmun 数据类型列
	 * @return
	 */
	public static <T> Map<String, Object> listToMap(List<T> source, String keyColmun, String valueColmun, String typeColmun) {
		Map<String, Object> raturn = new HashMap<String, Object>();
		if (source != null) {
			for (T t : source) {
				String key = null;
				Object value = null;
				String type = null;
				if (t instanceof Map) {
					@SuppressWarnings("unchecked")
					Map<String, Object> m = (Map<String, Object>) t;
					key = (String) m.get(keyColmun);
					value = m.get(valueColmun);
					if (StringUtils.isNotEmpty(typeColmun)) {
						type = (String) m.get(keyColmun);
					}
				} else {
					try {
						Method keyMethod = t.getClass().getMethod("get" + StringUtils.capitalize(keyColmun));
						Method valueMethod = t.getClass().getMethod("get" + StringUtils.capitalize(valueColmun));
						key = (String) keyMethod.invoke(t);
						value = valueMethod.invoke(t);
						if (StringUtils.isNotEmpty(typeColmun)) {
							Method typeMethod = t.getClass().getMethod("get" + StringUtils.capitalize(typeColmun));
							type = (String) typeMethod.invoke(t);
						}
					} catch (SecurityException e) {
						e.printStackTrace();
					} catch (NoSuchMethodException e) {
						e.printStackTrace();
					} catch (IllegalArgumentException e) {
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					} catch (InvocationTargetException e) {
						e.printStackTrace();
					}
				}
				if (type != null) {
					// TODO value 转型
					// Class<?> type = String.class;
					// if(StringUtils.isNotEmpty(typeColmun)){
					// type = Class.forName((String)m.get(typeColmun));
					// }
				}
				raturn.put(key, value);
			}
		}
		return raturn;
	}

	@SuppressWarnings("unchecked")
	public static <T> T convert(Object value, Class<T> clazz) {
		T valueObject = null;
		try {
			valueObject = (T) ConvertUtils.convert(value, clazz);
		} catch (ConversionException e) {
			log.error("输入参数[" + value + "]值于类型不匹配!");
			throw new RuntimeException(e);
		}
		return valueObject;
	}

	/**
	 * 如果类的属性存在注解，使用注解作为映射（属性名称与字段名称不一致的情况）
	 * <p>
	 * 
	 * @author WangNaiFei, WangNaiFei@senyint.com
	 * @date 2017年1月10日 下午11:24:37
	 * @version 1.0
	 * @param field
	 * @return
	 * @deprecated
	 */
	public static String getMappingColumnName(Field field) {
		String columnName = "";
		if (field.isAnnotationPresent(Column.class)) {
			Column column = field.getAnnotation(Column.class);
			columnName = column.name();
		} else {
			Log.INSTANCE.info("没找到JPA注解");
			columnName = field.getName();
		}
		return columnName;
	}

	/**
	 * 获取对象相应的属性值
	 * <p>
	 * 
	 * @author WangNaiFei, WangNaiFei@senyint.com
	 * @date 2017年1月10日 下午11:37:00
	 * @version 1.0
	 * @param pojo
	 * @param field
	 * @return
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws Exception
	 * @deprecated
	 */
	public static Object getPOJOGetterValue(Object pojo, Field field) throws IllegalArgumentException, IllegalAccessException {
		Object fieldValue = null;
		field.setAccessible(true);
		fieldValue = field.get(pojo);
		return fieldValue;
	}

	/**
	 * map中基本对象转字符串
	 * 
	 * @param m
	 * @return
	 */
	public static Map<String, String> mapObject2MapString(Map<String, Object> m) {
		HashMap<String, String> r = new HashMap<String, String>();
		for (String key : m.keySet()) {
			Object value = m.get(key);
			r.put(key, convert(value, String.class));
		}
		return r;
	}

	// public static List findAndInPackageByFile(String packageName, String
	// returnFileType, boolean recursive) {
	// String packageDirName = "";
	// if (packageName.contains("."))
	// packageDirName = packageName.replace('.', '/');
	// else
	// packageDirName = packageName;
	// List fileList = new ArrayList();
	// try {
	// String filePath;
	// for (Enumeration dirs =
	// Thread.currentThread().getContextClassLoader().getResources(packageDirName);
	// dirs.hasMoreElements(); findAndAddClassesInPackageByFile(packageName,
	// filePath, recursive, fileList, returnFileType)) {
	// URL url = (URL) dirs.nextElement();
	// filePath = URLDecoder.decode(url.getFile(), "UTF-8");
	// }
	//
	// } catch (IOException e) {
	// e.printStackTrace();
	// }
	// return fileList;
	// }
	//
	// private static void findAndAddClassesInPackageByFile(String packageName,
	// String packagePath, final boolean recursive, List xmlList, final String
	// returnFileType) {
	// File dir = new File(packagePath);
	// if (!dir.exists() || !dir.isDirectory())
	// return;
	// File dirfiles[] = dir.listFiles(new FileFilter() {
	// public boolean accept(File file) {
	// return recursive && file.isDirectory() || file.getName().endsWith((new
	// StringBuilder(".")).append(returnFileType).toString());
	// }
	// });
	// File afile[];
	// int j = (afile = dirfiles).length;
	// for (int i = 0; i < j; i++) {
	// File file = afile[i];
	// if (file.isDirectory()) {
	// findAndAddClassesInPackageByFile((new
	// StringBuilder(String.valueOf(packageName))).append(".").append(file.getName()).toString(),
	// file.getAbsolutePath(), recursive, xmlList, returnFileType);
	// } else {
	// String fileName = file.getName().substring(0, file.getName().length());
	// try {
	// xmlList.add((new StringBuilder(String.valueOf(packageName.replace(".",
	// "/")))).append('/').append(fileName).toString());
	// } catch (Exception e) {
	// e.printStackTrace();
	// }
	// }
	// }
	//
	// }

	/**
	 * @Title: marge
	 * @Description: 替换
	 * @author WangNaifei,WangNaifei@senyint.com
	 * @date 2018年6月1日 下午4:52:23
	 * @version 1.0
	 * @param target
	 * @param content
	 * @param overwriteAllExceptNull 不用content中的null值替换target
	 * @param checkNotExistField 验证target中没有content要替换的set方法
	 * @throws Exception
	 */
	public static void marge(Object target, Object content, boolean overwriteAllExceptNull, boolean checkNotExistField) throws Exception {
		Assert.notNull(target, "合并目标不能为空");
		Assert.notNull(content, "合并源不能为空");
		Method[] targetMethods = target.getClass().getDeclaredMethods();
		if (content instanceof Map) {
			@SuppressWarnings("unchecked")
			Map<String, Object> map = (Map<String, Object>) content;
			for (String key : map.keySet()) {
				String name = "get" + key;
				Method setterMethod = getSetterMethodByGetterMethodName(targetMethods, name);
				if (setterMethod == null) {
					if (checkNotExistField) {
						throw new Exception("field name[" + name + "] 在target中不存在 see param checkNotExistField");
					} else {
						continue;
					}
				}
				Object value = map.get(key);
				if (overwriteAllExceptNull && value == null) {
					continue;
				}
				Log.INSTANCE.debug("marge method:{},value:{}", setterMethod.getName(), value);
				setterMethod.invoke(target, convert(value, setterMethod.getParameterTypes()[0]));
			}
		} else {
			Method[] contentDeclaredMethods = content.getClass().getDeclaredMethods();
			for (Method contentMethod : contentDeclaredMethods) {
				String name = contentMethod.getName();
				if (isGetMethod(name)) {
					Method setterMethod = getSetterMethodByGetterMethodName(targetMethods, name);
					if (setterMethod == null) {
						if (checkNotExistField) {
							throw new Exception("field name[" + name + "] 在target中不存在 see param checkNotExistField");
						} else {
							continue;
						}
					}
					Object value = contentMethod.invoke(content);
					if (overwriteAllExceptNull && value == null) {
						continue;
					}
					Log.INSTANCE.debug("marge method:{},value:{}", setterMethod.getName(), value);
					setterMethod.invoke(target, value);
				}
			}
		}
	}

	private static Method getSetterMethodByGetterMethodName(Method[] methods, String getterName) {
		for (Method m : methods) {
			String settername = getterName.replaceFirst("^(get|is)", "set");
			if (m.getName().equalsIgnoreCase(settername)) {
				return m;
			}
		}
		return null;
	}

	/**
	 * @Title: fill
	 * @Description: 补充
	 * @author WangNaiFei,WangNaiFei@senyint.com
	 * @date 2018年7月19日 下午5:46:32
	 * @version 1.0
	 * @param target
	 * @param content
	 * @param checkNotExistField 验证target中没有content要替换的set方法
	 * @throws Exception
	 */
	public static void fill(Object target, Object content, boolean checkNotExistField) throws Exception {
		Assert.notNull(target, "补充目标不能为空");
		Assert.notNull(content, "补充源不能为空");
		Method[] targetMethods = target.getClass().getDeclaredMethods();
		Method[] contentDeclaredMethods = content.getClass().getDeclaredMethods();
		for (Method targetMethod : targetMethods) {
			String name = targetMethod.getName();
			if (isGetMethod(name)) {
				// 当前有值 不填充
				Object targetValue = targetMethod.invoke(target);
				if (targetValue != null) {
					continue;
				}
				Object contentValue = null;
				if (content instanceof Map) {
					@SuppressWarnings("unchecked")
					Map<String, Object> map = (Map<String, Object>) content;
					String nameNoGet = name.replaceFirst("^(get|is)", "");
					// 忽略大小写判断name是否在content中
					for (String key : map.keySet()) {
						if (nameNoGet.equalsIgnoreCase(key)) {
							contentValue = map.get(key);
						}
					}
				} else {
					// content 中没get方法 不填充
					Method contentGetterMethod = getGetterMethod(contentDeclaredMethods, name);
					if (contentGetterMethod == null) {
						continue;
					}
					// content 中没值 不填充
					contentValue = contentGetterMethod.invoke(content);
				}
				if (contentValue == null) {
					continue;
				}
				Method setterMethod = getSetterMethodByGetterMethodName(targetMethods, name);
				if (setterMethod == null) {
					if (checkNotExistField) {
						throw new Exception("set method of getter [" + name + "] 在target中不存在 see param checkNoSetterMethod");
					} else {
						continue;
					}
				}
				Log.debug(log, "marge method:{},value:{}", setterMethod.getName(), contentValue);
				setterMethod.invoke(target, contentValue);
			}
		}
	}

	private static Method getGetterMethod(Method[] methods, String getterName) {
		for (Method m : methods) {
			if (m.getName().equalsIgnoreCase(getterName)) {
				return m;
			}
		}
		return null;
	}

	public static void main(String[] args) {
		System.out.println(POJOUtil.convert(new Date(), Date.class));
		System.out.println(POJOUtil.convert("2013-03-02 21:12:35", Date.class));
		System.out.println(POJOUtil.convert("2013-03-02 21:12:35", String.class));
		System.out.println(POJOUtil.convert(new Date(), String.class));
		// String[] getterNames = { "getAbc", "isAbc", "getAbcis", "isAbcget", "abcget", "abcis" };
		// for (String getterName : getterNames) {
		// String settername = getterName.replaceFirst("^(get|is)", "set");
		// System.out.println(settername);
		// }
	}
}
